# 该文件提供一些工具函数/类
# encoding=utf-8


__author__ = 'lee'

import paramiko
import os
import time
import logging
import colorlog
import json


class Singleton(object):
    def __init__(self, cls):
        self._cls = cls
        self._instance = {}

    def __call__(self):
        if self._cls not in self._instance:
            self._instance[self._cls] = self._cls()
        return self._instance[self._cls]


@Singleton
class Logging(object):
    def __init__(self):
        pass

    def create(self):
        log_colors_config = {
            'D': 'blue',
            'I': 'green',
            'W': 'yellow',
            'E': 'red',
            'C': 'bold_red',
        }
        logger = logging.getLogger('logger_name')
        # 输出到控制台
        console_handler = logging.StreamHandler()
        # 输出到文件
        date_str = now_date_time()
        if not os.path.exists('log'):
            os.makedirs('log')
        log_path = f'log/{date_str}.log'
        file_handler = logging.FileHandler(
            filename=log_path, mode='a', encoding='utf8')
        # 日志级别，logger 和 handler以最高级别为准，不同handler之间可以不一样，不相互影响
        logging.addLevelName(logging.DEBUG, 'D')
        logging.addLevelName(logging.INFO, 'I')
        logging.addLevelName(logging.ERROR, 'E')
        logging.addLevelName(logging.WARNING, 'W')
        logger.setLevel(logging.DEBUG)
        console_handler.setLevel(logging.DEBUG)
        file_handler.setLevel(logging.INFO)
        # 日志输出格式
        file_formatter = logging.Formatter(
            fmt='[%(asctime)s.%(msecs)03d][%(levelname)s] : %(message)s',
            datefmt='%Y-%m-%d  %H:%M:%S'
        )
        console_formatter = colorlog.ColoredFormatter(
            fmt='%(log_color)s[%(asctime)s.%(msecs)03d][%(levelname)s] : %(message)s',
            datefmt='%Y-%m-%d  %H:%M:%S',
            log_colors=log_colors_config
        )
        console_handler.setFormatter(console_formatter)
        file_handler.setFormatter(file_formatter)
        # 重复日志问题：
        #  - 防止多次addHandler；
        #  - loggername 保证每次添加的时候不一样；
        #  - 显示完log之后调用removeHandler
        if not logger.handlers:
            logger.addHandler(console_handler)
            logger.addHandler(file_handler)

        console_handler.close()
        file_handler.close()
        return logger


class Logger():
    @staticmethod
    def info(content):
        logger = Logging().create()
        logger.info(content)

    @staticmethod
    def debug(content):
        logger = Logging().create()
        logger.debug(content)

    @staticmethod
    def warning(content):
        logger = Logging().create()
        logger.warning(content)

    @staticmethod
    def error(content):
        logger = Logging().create()
        logger.error(content)


class CommandExecute():
    def __init__(self, _hostip, _port, _username, _password):
        self.__hostip = _hostip
        self.__port = _port
        self.__username = _username
        self.__password = _password
        self.__sshclient = self.ssh_connect(
            self.__hostip, self.__port, self.__username, self.__password)
        self.__sftpclient = None

    def execute(self, command):
        try:
            _, stdout, _ = self.__sshclient.exec_command(command)
            for line in stdout:
                Logger.info(line.strip("\n"))
            return stdout.readlines()
        except Exception as e:
            Logger.error(
                f"ssh execute command {command} error, error message is {e}")
            return 0

    def execute_no_log(self, command):
        try:
            _, stdout, stderr = self.__sshclient.exec_command(command)
            for line in stdout:
                print(line.strip('\n'))
                if line.strip('\n') == '=====build  error=====':
                    for errline in stderr:
                        print(errline.strip('\n'))
                    return False
            return True
        except Exception as e:
            Logger.error(
                f"ssh execute command {command} error, error message is {e}")
            return False

    def execute_no_print(self, command):
        try:
            _, stdout, _ = self.__sshclient.exec_command(command)
            return stdout.readlines()
        except Exception as e:
            Logger.error(
                "ssh execute command %s error, error message is %s" % (command, e))
            return ""

    def close(self):
        self.__sshclient.close()
        try:
            self.__sftpclient.close()
            Logger.info("close ssh or sftp service")
        except:
            pass

    def ssh_connect(self, _host, _port, _username, _password):
        try:
            _ssh_fd = paramiko.SSHClient()
            _ssh_fd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            _ssh_fd.connect(_host, _port, _username, _password, timeout=30)
            _ssh_fd.get_transport().set_keepalive(60)
            return _ssh_fd
        except Exception as e:
            Logger.error('ssh %s@%s passwd:%s, error message is %s' %
                         (_username, _host, _password, e))

    def sftp_open(self):
        if self.__sftpclient is None:
            self.__sftpclient = self.__sshclient.open_sftp()

    def sftp_put(self, _put_from_path, _put_to_path):
        Logger.info(
            f"sftp transform ==> put {_put_from_path} to {_put_to_path}")
        self.sftp_open()
        self.__sftpclient.put(_put_from_path, _put_to_path)
        self.__sftpclient.close()
        self.__sftpclient = None

    def sftp_get(self, _remote_path, _local_path):
        Logger.info(
            f"sftp transform ==> get from {_remote_path} to {_local_path}")
        self.sftp_open()
        self.__sftpclient.get(_remote_path, _local_path)
        self.__sftpclient.close()
        self.__sftpclient = None


def command_execute(command):
    Logger.info(f'command execute: {command}')
    text = os.popen(command)
    result = text.readlines()
    return result


def now_date_time():
    now = int(round(time.time()*1000))
    date = time.strftime('%Y-%m-%d', time.localtime(now/1000))
    return date


def parse_json(path):
    # 解析json文件
    with open(path) as f:
        data = json.load(f)
    return data
